<?
/**
 * Webapp Class
 */
(basename($_SERVER['PHP_SELF']) == basename(__FILE__)) && die('Access Denied');

class webapp {

/**
 * App modules
 */
public $auth = null;
public $html = null;
public $log  = null;
public $sess = null;

/**
 * Classes the modules use
 */
private $classAuth = 'appAuth';
private $classHtml = 'appHtml';
private $classLog  = 'Log';
private $classSess = 'appSession';

/**
 * HTML Module Settings
 * setAppUri()
 */
private $appUri = './';

/**
 * Track module names that have been loaded
 * @see moduleLoad(), moduleLoaded()
 */
private $modulesLoaded = array();

/**
 * Is the current requestor authenticated?
 * @return bool (from sess->isAuthenticated)
 */
public function authenticated() {
	return $this->sess->isAuthenticated();
	}

/**
 * If the current requestor is not authenticated, dump them to the login prompt
 * @return void
 */
public function authenticationRequired() {
	if (!($this->authenticated() === true)) {
		die($this->loginPrompt());
		}
	}

/**
 * Load an applet if it exists
 * @param string $applet
 * @param bool $loadIndex
 * @return void
 */
public function getApplet($applet = null) {
	global $_APP;
	if (!$this->isApplet($applet)) { die('access denied'); }
	$ad        = WEB_APP_DIR . '/applets/' . $applet;
	$td        = $ad . '/templates';
	$index     = $ad . '/applet.php';
	$strings   = $ad . '/strings.php';
	$functions = $ad . '/functions.php';
	if (is_file($strings))   { include($strings); }
	if (is_file($functions)) { include($functions); }
	if (is_dir($td)) {
		$this->html->setTemplateDir($td);
		}
	die(include($index));
	}

/**
 * Obtain display name of currently authenticated user.
 * @return string on success
 * @return bool false on failure
 */
public function getAuthenticatedUserDisplayName() {
	return $this->sess->getUserData('DisplayName');
	}

/**
 * Obtain numeric UID of currently authenticated user.
 * @return int $uid on success
 * @return bool false on failure
 */
public function getAuthenticatedUserId() {
	return $this->sess->getUserData('Id');
	}

/**
 * Obtain username of currently authenticated user.
 * @return string username on success
 * @return bool false on failure
 */
public function getAuthenticatedUserName() {
	return $this->sess->getUserData('Login');
	}

/**
 * Redirect browser to app root uri
 * @return void
 */
public function goHome() {
	die(header('Location: ' . $this->appUri));
	}

/**
 * Is the requested applet valid?
 * @param string $applet
 * @return bool
 */
public function isApplet($applet = null) {
	if (!is_string($applet)) { return false; }
	$ad = WEB_APP_DIR . '/applets/' . $applet;
	if (!is_dir($ad)) { return false; }
	return (is_file($ad . '/applet.php'));
	}

/**
 * Process applet requests
 */
public function loadRequestedApplets() {
	$app = $this->html->getAppletRequest();
	$t1 = (is_string($app));
	$t2 = ($t1 and $this->isApplet($app));
	if ($t1 and $t2) {
		die($this->getApplet($app));
		}
	}

/**
 * Process a login request
 * @return void
 */
public function login() {
	// no martians
	if ($this->authenticated()) {
		$this->logout(false);
		$m = 'Previous session has been logged out, try again.';
		$this->html->addError($m);
		die($this->loginPrompt());
		}

	// require our modules
	$this->moduleRequired('auth');

	// Did user specify credentials (via post)?
	$un = $this->html->getRequestVar(WA_QS_USERNAME, true);
	$pw = $this->html->getRequestVar(WA_QS_PASS1, true);

	if (!strlen($un)) {
		$this->html->addError('Must specify a Login username.');
		die($this->loginPrompt());
		}
	if (!strlen($pw)) {
		$this->html->addError('Must specify a password.');
		die($this->loginPrompt());
		}

	// objects we need
	$uo = getDbo('User');

	// Pass the authentication module a User object and the credentials
	// Spice up the password if possible.
	if (defined('PASS_SALT')) {
		$md5pass = md5(PASS_SALT . ':' . $pw);
		} else {
		$md5pass = md5($pw);
		}

	// submit credentials to authentication method and handle the results
	$user = $this->auth->authenticate($un, $md5pass, $uo);
	if (!is_array($user)) {
		$err = 'login fail: user(' . $un . ') ';
		switch($user) {
			case WA_FAIL_INVALID_CREDS:
				$this->log->log_audit($err . 'invalid credentials');
				$this->html->addError('Invalid credentials, try again.');
				die($this->loginPrompt());
			break;
			case WA_FAIL_NO_USERNAME:
				$this->html->addError('Please enter username and try again');
				die($this->loginPrompt());
			break;
			case WA_FAIL_NO_PASSWORD:
				$this->html->addError('Please enter password and try again.');
				die($this->loginPrompt());
			break;
			case WA_FAIL_EXPIRED:
				$this->log->log_audit($err . 'account expired');
				$this->html->addError('Your account is expired, contact an administrator.');
				die($this->loginPrompt());
			break;
			case WA_FAIL_DISABLED:
				$this->log->log_audit($err . 'account disabled');
				$this->html->addError('Your account is disabled, contact an administrator.');
				die($this->loginPrompt());
			break;
			default:
				$this->log->log_audit($err . $user);
				$this->html->addError('Unknown error, try again.');
				die($this->loginPrompt());
			break;
			}
		}

	// create session and handle any errors
	$rc = $this->sess->create($user);
	if (!($rc === true)) {
		$this->log->log_err('user(' . $un . ') session init failure: ' . $rc);
		$this->logout(false);
		$this->html->addError('Failure initializing session, contact an administrator.');
		die($this->loginPrompt());
		}

	// ok, we are good to go, redirect them back home
	$this->log->log_audit('login');
	die($this->goHome());
	}

/**
 * Die with a login prompt
 */
public function loginPrompt() {
	$this->html->setTemplateDir(WEB_APP_TMP);
	die($this->html->getTemplate('login.form.php'));
	}

/**
 * Process a logout request
 */
public function logout($goHome = true) {
	if ($this->authenticated() === true) {
		$this->log->log_audit('logout');
		}
	// drop session
	$this->sess->sessionDestroy();
	// redirect home...
	if ($goHome) {
		die($this->goHome());
		}
	}

/**
 * Load a webapp module
 * @param string $module
 * @return bool
 */
private function moduleLoad($module = null) {
	$mod_args = null;
	switch($module) {
		case 'auth': $modClass = $this->classAuth; break;
		case 'html': $modClass = $this->classHtml; break;
		case 'log':  $modClass = $this->classLog;  break;
		case 'sess': $modClass = $this->classSess; break;
		default:     return false;                 break;
		}
	// Already loaded
	if (in_array($module, $this->modulesLoaded)) {
		if ($this->$module instanceof $modClass) { return true; }
		}
	// Property has to exist
	if (!property_exists($this,$module)) { return false; }
	// Class has to already be loaded
	if (!class_exists($modClass, false)) { return false; }
	// If it is a dbo object, call getDbo() to get it.
	if (is_subclass_of($modClass, 'HistoryTimelineDbo')) {
		$this->$module = getDbo($modClass);
		} else {
		// Attempt to load it...
		$this->$module = new $modClass();
		}
	if (!($this->$module instanceof $modClass)) { return false; }
	// Handle customization
	switch($module) {
		case 'html':
			$this->appUri = WEB_APP_HTML_URI;
			$this->html->setCharset(WEB_APP_HTML_CHARSET);
			$this->html->setContextMenu(WEB_APP_HTML_CONTEXT_MENU);
			$this->html->addCss(WEB_APP_HTML_CSS);
			$this->html->setDebug(WEB_APP_HTML_DEBUG);
			$this->html->setTemplateDir(WEB_APP_TMP);
			$this->html->setDoctype(WEB_APP_HTML_DOCTYPE);
			// $this->html->addJs(WEB_APP_HTML_JS);
			$this->html->setUri(WEB_APP_HTML_URI);
			$this->html->addMeta(WEB_APP_HTML_META);
			$this->html->setTitle(WEB_APP_NAME);
		break;
		case 'log':
			$this->log->setLogApp(WEB_APP_NAME);
			$this->log->setLogDatabase();
		break;
		}
	// track modules loaded
	$this->modulesLoaded[] = $module;
	return true;
	}

/**
 * Attempt to load specified module(s) and die on any failures
 * Accepts single module name, comma separated list of module names, or array
 * of module names.  If a failure is experienced loading any of the modules,
 * a die() is called with an error message.
 * @param mixed $modules
 * @return mixed
 */
public function moduleRequired($modules=null) {
	$msg = 'FATAL ERROR: failed to load required module';
	if (empty($modules)) { die($msg); }
	if (!is_array($modules)) {
		$modules = explode(',',$modules);
		}
	if (!is_array($modules) or count($modules) < 1) {
		die($msg);
		}
	foreach($modules as $module) {
		if (!($this->moduleLoad($module) === true)) {
			die($msg . ': ' . $module);
			}
		}
	return true;
	}

/**
 * Die with a not authorized page.
 */
public function notAuthorized() {
	$this->html->setTemplateDir(WEB_APP_TMP);
	die($this->html->getTemplate('not.authorized.php'));
	}

/**
 * Process a password change request
 * @return void
 */
public function passchange() {
	// Huh?
	$this->authenticationRequired();

	// Set template vars early on...
	$this->html->setVar('minLength',      $this->auth->getPasswordMinimumLength());
	$this->html->setVar('pcLower',        $this->auth->getPasswordCharsLower());
	$this->html->setVar('pcNumber',       $this->auth->getPasswordCharsNumber());
	$this->html->setVar('pcSpecial',      $this->auth->getPasswordCharsSpecial());
	$this->html->setVar('pcUpper',        $this->auth->getPasswordCharsUpper());
	$this->html->setVar('requireLower',   $this->auth->getPasswordRequireLower());
	$this->html->setVar('requireNumber',  $this->auth->getPasswordRequireNumber());
	$this->html->setVar('requireSpecial', $this->auth->getPasswordRequireSpecial());
	$this->html->setVar('requireUpper',   $this->auth->getPasswordRequireUpper());

	// Did user their new password
	$pw1 = $this->html->getRequestVar(WA_QS_PASS1, true);
	$pw2 = $this->html->getRequestVar(WA_QS_PASS2, true);
	if (!strlen($pw1) or !strlen($pw2)) {
		die($this->html->getTemplate('password.change.form.php'));
		}

	// Passwords match?
	if (strcmp($pw1, $pw2) != 0) {
		$this->html->addError('The passwords do not match, try again.');
		die($this->html->getTemplate('password.change.form.php'));
		}

	// Get our modules
	$this->moduleRequired('auth');

	// Get a user object and populate it.
	$uo = getDbo('User');

	// Do the deed
	$rc = $this->auth->passwordChange($uo, $this->sess, $pw1);
	if (!($rc === true)) {
		$this->html->addError($rc);
		die($this->html->getTemplate('password.change.form.php'));
		}

	// Good to go, redirect them home...
	die($this->goHome());
	}

/**
 * Get return url via session
 * @return string
 */
public function returnUrlGet() {
	if (!isset($_SESSION[WEB_APP_SESS_KEY]['returnUrl'])) { return './'; }
	return $_SESSION[WEB_APP_SESS_KEY]['returnUrl'];
	}

/**
 * Set return url in session via referrer if not already set
 * @param string url
 * @return bool
 */
public function returnUrlSet($url = null) {
	if (!is_string($url) or !strlen($url)) { return false; }
	$_SESSION[WEB_APP_SESS_KEY]['returnUrl'] = $url;
	return true;
	}
}
?>
